
/** dts configuration
 &i2c0 {
	hy16009a: hy16009a@53 {
		compatible = "hy16009a";
		int-gpio = <&gpio4 RK_PD1 IRQ_TYPE_LEVEL_HIGH>;
		reg = <0x53>;
		keys = < KEY_VOLUMEUP KEY_VOLUMEDOWN KEY_POWER>;
		pinctrl-names = "default";
		pinctrl-0 = <&hy16009a_int &hy16009a_led>;
		pwms = <&pwm4 0 25000 0>;
		led-ctl-gpios =<&gpio3 RK_PB7 GPIO_ACTIVE_HIGH
			&gpio3 RK_PB6 GPIO_ACTIVE_HIGH
			&gpio3 RK_PB5 GPIO_ACTIVE_HIGH
			&gpio3 RK_PB4 GPIO_ACTIVE_HIGH
			&gpio3 RK_PB3 GPIO_ACTIVE_HIGH
			&gpio3 RK_PB2 GPIO_ACTIVE_HIGH
			&gpio3 RK_PB1 GPIO_ACTIVE_HIGH
			&gpio3 RK_PB0 GPIO_ACTIVE_HIGH
			&gpio3 RK_PA7 GPIO_ACTIVE_HIGH
			&gpio3 RK_PA6 GPIO_ACTIVE_HIGH
			&gpio3 RK_PA5 GPIO_ACTIVE_HIGH
			&gpio3 RK_PA4 GPIO_ACTIVE_HIGH
			>;
	};
 };
 */

/**
 *  create /dev/hy16009a 
 *  cat /dev/hy16009a will show current position
 *  echo 255 255 > /dev/hy16009a
 */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/input.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/pwm.h>

#define COMPATILE_NAME "hy16009a"
#define DEV_NAME  "hy16009a"
#define MAX_KEY_SUPPORT 6

#define MAX_BRIGHTNESS 255
#define CALIBRATE_MASK (1UL << 7)
#define WHEEL_TOUCH_MASK (1UL << 4)

struct hy16009a_priv {
	struct i2c_client *client;
	struct input_dev *input;
	struct pwm_device *pwm;
	struct miscdevice miscdev;
	struct delayed_work work;
	int irq;
	int irq_pin;
	int loc;  // slide position
	unsigned int  keys[MAX_KEY_SUPPORT];

};

static const struct i2c_device_id hy16009a_id[] = {
	{ COMPATILE_NAME, 0 },
	{ },
};
MODULE_DEVICE_TABLE(i2c, hy16009a_id);

#ifdef CONFIG_OF
static const struct of_device_id hy16009a_of_table[] = {
	{ .compatible = COMPATILE_NAME },
	{ }
};
MODULE_DEVICE_TABLE(of, hy16009a_of_table);
#endif

static int key_idx[] = {0, 1,2,3, -1, 4, 5, -1};

static int debug_enable = 0;
module_param(debug_enable,int, 0644);

// function define in led_controller.c @{
extern int led_ctl_parse_dt(struct device *dev);
extern int led_group_control(int pos, int max_pos);
// @}

static inline void send_key(struct input_dev *input ,unsigned int code)
{
	pr_info("%s code=%u\n", __func__, code);
	input_report_key(input, code, 1);
	input_sync(input);
	input_report_key(input, code, 0);	
	input_sync(input);
}

static void  pwm_set_brightness(struct pwm_device *pwm, int brightness)
{
	struct pwm_state state;
	u64 duty_cycle;
	pwm_get_state(pwm, &state);
	if (brightness > 0) {
		duty_cycle = brightness;
		duty_cycle *= state.period;
		do_div(duty_cycle, (MAX_BRIGHTNESS+1));
		state.duty_cycle = duty_cycle;  
		state.enabled = true;
	}else {
		state.enabled = false;
		state.duty_cycle = 0;		
	}
	pwm_apply_state(pwm, &state);
}

static void process_data(struct hy16009a_priv * chip, char buff[])
{
	int i, mask;
	int key_pressed = 0;
	dev_info(&chip->client->dev, "data[0]=%x, [1]=%x\n", buff[0], buff[1]);
	if(buff[0] & CALIBRATE_MASK){
		for(i=0, mask=1; i<7; i++, mask <<=1){
			if((buff[0] & mask)&0xff){
				if(key_idx[i] >=0){
					key_pressed = 1;
					send_key(chip->input, chip->keys[key_idx[i]]);
				}
			}
		}
		// key and location happened, we ignore location
		if(key_pressed){
			return;
		}
		if(buff[0] & WHEEL_TOUCH_MASK){
			// TODO: call location 
			chip->loc =buff[1] & 0xff;
			dev_info(&chip->client->dev, "location=%d", chip->loc);
			led_group_control(chip->loc, MAX_BRIGHTNESS);
			pwm_set_brightness(chip->pwm, chip->loc);
		}

	}else{
		dev_info(&chip->client->dev, "data not calibrate, ignore");
	}
}

static int misc_release(struct inode *inode, struct file *file)
{
	struct hy16009a_priv *chip = container_of(file->private_data, struct hy16009a_priv, miscdev);
	dev_info(&chip->client->dev, "%s +++\n", __func__);
	return 0;
}

static int misc_open(struct inode *inode, struct file *file)
{
	struct hy16009a_priv *chip = container_of(file->private_data, struct hy16009a_priv, miscdev);
	dev_info(&chip->client->dev, "%s +++\n", __func__);
	return 0;
}

static ssize_t misc_read(struct file *file, char __user *buf,
				 size_t count, loff_t *pos)
{
	struct hy16009a_priv *chip = container_of(file->private_data, struct hy16009a_priv, miscdev);
	size_t sz;
	if(!chip){
		return -1;
	}
	dev_info(&chip->client->dev, "%s +++\n", __func__);
	sz = sizeof(chip->loc);
	if(*pos >= sz)
		return 0;
	if(put_user(chip->loc, buf)){
		dev_err(&chip->client->dev, "%s  put_user fail\n", __func__);
		return -1;
	}
	*pos += sz;
	return sz;
}

static ssize_t misc_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{
	#define MAX_DATA_SIZE 16
	char data[MAX_DATA_SIZE];
	char tmp[2];
	struct hy16009a_priv *chip = container_of(file->private_data, struct hy16009a_priv, miscdev);
	dev_info(&chip->client->dev, "%s +++\n", __func__);
	memset(data, 0, sizeof(data));
	if(count >= MAX_DATA_SIZE)
		count = MAX_DATA_SIZE -1;
	if(copy_from_user(data, buf, count)){
		dev_err(&chip->client->dev, "copy_from_user from fail\n");
	}else{
		int v1, v2;
		if(sscanf(data, "%d %d", &v1, &v2) != 2){
			dev_err(&chip->client->dev, "write need two int,eg: 255 255\n");
		}else{
			tmp[0] = v1 & 0xff; tmp[1] = v2 & 0xff;
			process_data(chip, tmp);
		}	
	}
	return count;
}


static const struct file_operations hy16009a_misc_fops = {
	.owner = THIS_MODULE,
	.read = misc_read,
	.open = misc_open,
	.write = misc_write,
	.release = misc_release,
};




static void work_func(struct work_struct *work)
{
	struct delayed_work *dwork = to_delayed_work(work);
	struct hy16009a_priv *priv = container_of(dwork, struct hy16009a_priv,work);
	char buff[2];
	int err;
	dev_info(&priv->client->dev, "%s++", __func__);
	if((err=i2c_master_recv(priv->client, buff, sizeof(buff)))<0){
		dev_err(&priv->client->dev, "i2c_master_recv fail: err=%d\n", err);
	}else{
		process_data(priv, buff);
	}
	enable_irq(priv->irq);
}
static irqreturn_t irq_handler(int irq, void *dev_id)
{
	struct hy16009a_priv *priv = dev_id;
	disable_irq_nosync(priv->irq);
    if (!delayed_work_pending(&priv->work)){
    	schedule_delayed_work(&priv->work, msecs_to_jiffies(100));
    }
	return IRQ_HANDLED;
}

static int hy16009a_probe(struct i2c_client *client,
				   const struct i2c_device_id *id)
{
	struct device_node *np;
	struct hy16009a_priv *chip;
	enum of_gpio_flags irq_flags;
	int ret;
	int cnt;
	struct pwm_state state;

	dev_info(&client->dev, "%s enter\n", __func__);
	np = client->dev.of_node;
	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
	if (chip == NULL)
		return -ENOMEM;
	chip->client = client;
	i2c_set_clientdata(client, chip);
	chip->pwm = devm_pwm_get(&client->dev, NULL);
	if (IS_ERR(chip->pwm)) {
		ret = PTR_ERR(chip->pwm);
		if (ret == -EPROBE_DEFER){
			dev_info(&client->dev, "defer probe to pwm");
			return -EPROBE_DEFER;
		}else {
			dev_err(&client->dev, "unable to request PWM\n");
			return -ENODEV;
		}	
	}
	pwm_init_state(chip->pwm, &state);
	dev_info(&client->dev, "pwm info: period=%llu, duty_cycle=%llu, polor=%d\n", \
		state.period, state.duty_cycle, state.polarity);
	ret = pwm_apply_state(chip->pwm, &state);
	if (ret) {
		dev_err(&client->dev, "failed to apply initial PWM state: %d\n",ret);
	}
#if 0
	chip->irq  = irq_of_parse_and_map(np,0);
	if (chip->irq == NO_IRQ) {
	    dev_err(&client->dev,"No interrupt found in the device tree\n");
	    return -EIO;
	}
#else
	chip->irq_pin=of_get_named_gpio_flags(np, "int-gpio", 0, &irq_flags);
    if (gpio_is_valid(chip->irq_pin)) {
        ret = devm_gpio_request_one(&client->dev, chip->irq_pin, GPIOF_OUT_INIT_HIGH, DEV_NAME);
        if (ret != 0) {
            dev_err(&client->dev, " irq pin error\n");
            return -EIO;
        }

    } else {
        dev_info(&client->dev, "irq pin invalid\n");
    }
    if((chip->irq=gpio_to_irq(chip->irq_pin))<=0){
    	dev_err(&client->dev, " gpio_to_irq error\n");
    	return -EIO;
    }
#endif
    INIT_DELAYED_WORK(&chip->work, work_func);
    ret = devm_request_threaded_irq(&client->dev, chip->irq, NULL, irq_handler, IRQ_TYPE_LEVEL_LOW | IRQF_ONESHOT, client->name, chip);
	if(ret < 0){
		dev_err(&client->dev, "request irq fail");
		return -ENOMEM;
	}
	// parse key info
	if((cnt=of_property_count_u32_elems(np, "keys"))<0){
		dev_info(&client->dev, "no key support");
	}else{
		if(cnt > MAX_KEY_SUPPORT)
			cnt = MAX_KEY_SUPPORT;
		if((ret=of_property_read_u32_array(np, "keys", chip->keys, cnt))<0){
			dev_err(&client->dev, "read key info fail");
		}else{
			int i;
			struct input_dev * input = devm_input_allocate_device(&client->dev);
			if(!input){
				dev_err(&client->dev, "input_allocate_device failed");
				return -ENOMEM;
			}
			input->name = DEV_NAME;
			input->phys = DEV_NAME"/input0";

			input->id.bustype = BUS_HOST;
			input->id.vendor = 0x0001;
			input->id.product = 0x0001;
			input->id.version = 0x0100;

			__set_bit(EV_KEY, input->evbit);
			for(i=0; i<cnt; i++){
				input_set_capability(input, EV_KEY, chip->keys[i]);
			}
			if(input_register_device(input)<0){
				dev_err(&client->dev, "input_register_device fail");
			}
			chip->input = input;
		}
	}

	led_ctl_parse_dt(&client->dev);

	chip->miscdev.minor = MISC_DYNAMIC_MINOR;
	chip->miscdev.name = DEV_NAME;
	chip->miscdev.fops = &hy16009a_misc_fops;
	ret = misc_register(&chip->miscdev);
	if (ret) {
		dev_err(&client->dev, "failed to register misc dev: %d\n", ret);
	}

	return 0;

}

static int hy16009a_remove(struct i2c_client *client)
{
//	int ret = 0;
	struct hy16009a_priv *chip = i2c_get_clientdata(client);
	dev_info(&client->dev, "%s enter\n", __func__);
	cancel_delayed_work_sync(&chip->work);
	misc_deregister(&chip->miscdev);
	return 0;
}

void hy16009a_shutdown(struct i2c_client *client)
{
//	int ret = 0;
//	struct hy16009a_priv *chip = i2c_get_clientdata(client);
	dev_info(&client->dev, "%s enter\n", __func__);
	
}

static struct i2c_driver hy16009a_driver = {
	.driver = {
		.name		= "hy16009a",
		.owner		= THIS_MODULE,
		.of_match_table	= of_match_ptr(hy16009a_of_table),
	},
	.probe		= hy16009a_probe,
	.remove		= hy16009a_remove,
	.id_table	= hy16009a_id,
	.shutdown = hy16009a_shutdown,
};

static int __init hy16009a_init(void)
{
	return i2c_add_driver(&hy16009a_driver);
}
/* register after i2c postcore initcall and before
 * subsys initcalls that may rely on these GPIOs
 */
#if 1
subsys_initcall_sync(hy16009a_init);
#else
module_init(hy16009a_init);
#endif
static void __exit hy16009a_exit(void)
{
	i2c_del_driver(&hy16009a_driver);
}
module_exit(hy16009a_exit);

MODULE_AUTHOR("tj <tj_wust@163.com>");
MODULE_DESCRIPTION("i2c demo driver");
MODULE_LICENSE("GPL");
